import numpy as np
import matplotlib.pyplot as plt
from scipy.spatial import ConvexHull
import math
import pprint


# 获取obj文件的所有顶点信息，返回顶点列表，[x, y, z, r, g, b]，前三元素为float
def getAllVertices(filename="body_part.obj", startLine=12, endLine=12511):
  with open(filename,'r') as file:
    lines = file.readlines()
    vertices_info = lines[startLine - 1:endLine]
    for index, value in enumerate(vertices_info):
      vertices_info[index] = value.split(" ")[1:]
      for i in range(3):
        vertices_info[index][i] = float(vertices_info[index][i])
      vertices_info[index][5] = vertices_info[index][5].replace('\n', '')
    return vertices_info


# 获取模型，返回参数为高度，底部高度值和顶部高度值
def _getHeight(point_list, top_point_index = 12482, bottom_point_index = 225):
  return point_list[top_point_index][2] - point_list[bottom_point_index][2], point_list[bottom_point_index][2]

# 获取模型肩宽
def _getShoulderWidth(point_list, left_point_index = 10737, right_point_index = 10559):
  return point_list[left_point_index][0] - point_list[right_point_index][0]

# 筛选指定部位的顶点，part传英文部位名，筛选后返回index，data两个列表，注意字典的更新
def _filter_specific_vertices(point_list, part):
  index_list = []
  filter_list = []
  body_part_rgb = {'chest': ['1.000000', '0.000000', '0.000000'], 'waist': ['0.000000', '0.000000', '1.000000'], 'hips': ['1.000000', '1.000000', '0.000000'], 'left-leg': ['0.666667', '0.000000', '1.000000'], 'right-leg': ['1.000000', '0.000000', '1.000000'], 'left-shoulder': ['0.329412', '0.000000', '0.494118'], 'left-arm': ['0.000000', '1.000000', '1.000000']}
  for index, value in enumerate(point_list):
    if value[3] == body_part_rgb[part][0] and value[4] == body_part_rgb[part][1] and value[5] == body_part_rgb[part][2]:
      index_list.append(index)
      filter_list.append(value[:3])
  
  return index_list, filter_list

# 根据设定的高度，以及步长（单边的step），截取区间内的点，返回其index，x坐标和y坐标，注意常量的更新
def get_section_list(point_list, part, step):
  # 配置常量
  body_part_proportion = {'chest': 0.71, 'waist': 0.62, 'hips': 0.51, 'left-leg': 0.44, 'right-leg': 0.44}

  height, bottom_val = _getHeight(point_list)
  # print("height:" + str(height))
  index_list, part_list = _filter_specific_vertices(point_list, part)

  cut_height = height * body_part_proportion[part] + bottom_val
  up_limit = cut_height + step
  down_limit = cut_height - step
  res_index_list = []
  res_vertices_list = []

  for index in range(len(index_list)):
    if part_list[index][2] < up_limit and part_list[index][2] > down_limit:
      res_index_list.append(index_list[index])
      res_vertices_list.append([part_list[index][0], part_list[index][1]])

  # 凸包处理
  res_hull = ConvexHull(res_vertices_list)
  # res_hull_list = hull.vertices.tolist()  # 要闭合必须再回到起点[0]
  # hull1.append(hull1[0])

  # np的array处理，方便后面进行x，y轴的拆解
  res_vertices_list = np.array(res_vertices_list)

  return res_index_list, res_vertices_list, res_hull


# 画截面
def show_section_shape(point_list, title="section shape", xlabel="value of X", ylabel="value of Y", hull=None):
  plt.figure()
  plt.title(title)
  plt.xlabel(xlabel)
  plt.ylabel(ylabel)
  plt.plot(point_list[:, 0], point_list[:, 1], 'o')
  if hull != None:
    for simplex in hull.simplices:
      plt.plot(point_list[simplex,0], point_list[simplex,1], 'k-')
  plt.show()


# 获取截面的尺寸信息
def get_section_length(point_list, hull):
  length = 0
  for simplex in hull.simplices:
    # x轴，y轴平面前后点的欧式距离累加
    length += np.sqrt(np.sum(np.square(np.array([point_list[simplex,0][0],point_list[simplex,1][0]])-np.array([point_list[simplex,0][1],point_list[simplex,1][1]]))))
  width = max(point_list[:, 0]) - min(point_list[:, 0])
  thickness = max(point_list[:, 1]) - min(point_list[:, 1])
  return length, width, thickness


# 保存index数据到txt
def text_save(filename, data): # filename为写入文件的路径，data为要写入数据列表.
  file = open(filename,'w')
  for i in range(len(data)):
    s = str(data[i]).replace('[','').replace(']','')#去除[],这两行按数据不同，可以选择
    s = s.replace("'",'').replace(',','') +'\n'   #去除单引号，逗号，每行末尾追加换行符
    file.write(s)
  file.close()
  print(filename + "保存成功") 


# 逆时针旋转n角度，degree为弧度制
def points_rotate_n_degree(point_set, degree):
  res_set = []
  for i in range(len(point_set)):
    res_x = point_set[i][0] * math.cos(degree) - point_set[i][1] * math.sin(degree)
    res_y = point_set[i][0] * math.sin(degree) + point_set[i][1] * math.cos(degree)
    res_z = point_set[i][2]
    res_set.append([res_x, res_y, res_z])
  return np.array(res_set)

def get_spring_points(model_file, startLine=0, endLine=12500):
  vertices_info = []
  with open(model_file,'r') as file:
    lines = file.readlines()
    vertices_info = lines[startLine:endLine]
    for i in range(len(vertices_info)):
      vertices_info[i] = vertices_info[i].split("   ")[1:]
      vertices_info[i][0] = vertices_info[i][0].replace(' ', '')
      vertices_info[i][1] = vertices_info[i][1].replace(' ', '')
      vertices_info[i][2] = vertices_info[i][2].replace('\n', '')
      for j in range(3):
        vertices_info[i][j] = float(vertices_info[i][j])
  return vertices_info

# 根据部位的索引数据读其他样本的点集数据
def load_and_get_section_info(part, point_set):
  # 读部位点集索引列
  filename = 'index_data/' + part + '.txt'
  section_vertice_index = []
  file = open(filename, 'r')
  while True:
    line = file.readline()
    if line == '':
      break
    section_vertice_index.append(int(line))
  file.close()

  # 提取该模型相应的点列
  section_vertice_value = []
  for i in range(len(section_vertice_index)):
    section_vertice_value.append([point_set[section_vertice_index[i]][0], point_set[section_vertice_index[i]][1], point_set[section_vertice_index[i]][2]])
  
  # 凸包处理
  section_vertice_value = np.array(section_vertice_value)
  # 注意凸包不能包含z轴信息
  res_hull = ConvexHull(section_vertice_value[:, :2])

  return section_vertice_value, res_hull


# 获取臂长和腿长的尺寸信息
def get_arm_leg_length(point_set):
  control_point_set, hull = load_and_get_section_info('arm_length', point_set)
  arm_length = 0
  leg_length = 0
  for i in range(len(control_point_set) - 1):
    # y轴，z轴平面上的欧氏距离累加
    arm_length += math.sqrt((control_point_set[i][1] - control_point_set[i + 1][1]) ** 2 + (control_point_set[i][2] - control_point_set[i + 1][2]) ** 2)
  control_point_set, hull = load_and_get_section_info('leg_length', point_set)
  for i in range(len(control_point_set) - 1):
    # y轴，z轴平面上的欧氏距离累加
    leg_length += math.sqrt((control_point_set[i][1] - control_point_set[i + 1][1]) ** 2 + (control_point_set[i][2] - control_point_set[i + 1][2]) ** 2)
  return arm_length, leg_length

# 生成index记录文件，根据需要操作注释
def save_part_index2txt():
  point_list = getAllVertices()
  index_list, vertices_list, hull = get_section_list(point_list, 'chest', 0.005)
  # show_section_shape(vertices_list, hull=hull)
  text_save('index_data/chest.txt', index_list)
  index_list, vertices_list, hull = get_section_list(point_list, 'waist', 0.005)
  text_save('index_data/waist.txt', index_list)
  index_list, vertices_list, hull = get_section_list(point_list, 'hips', 0.005)
  text_save('index_data/hips.txt', index_list)
  index_list, vertices_list, hull = get_section_list(point_list, 'left-leg', 0.0075)
  text_save('index_data/left-leg.txt', index_list)
  # print(len(index_list))
  index_list, vertices_list, hull = get_section_list(point_list, 'right-leg', 0.007)
  text_save('index_data/right-leg.txt', index_list)
  # print(len(index_list))

# 获取莫模型的特征尺寸信息
def get_model_all_measure_value(model_file):
  # 根据index文件读围度
  point_set = get_spring_points(model_file)
  # 旋转位置使y轴对身体中心
  point_set = points_rotate_n_degree(point_set, 0.285 * math.pi)
  # 字典记录各尺寸数据
  measure_dir = {}
  measure_content = ['chest', 'waist', 'hips', 'right-leg']
  # measure_content = ['chest']
  for i in range(len(measure_content)):
    point_list, hull = load_and_get_section_info(measure_content[i], point_set)
    length, width, thickness = get_section_length(point_list, hull)
    measure_dir[measure_content[i] + '_length'] = int(length * 1000)
    measure_dir[measure_content[i] + '_width'] = int(width * 1000)
    measure_dir[measure_content[i] + '_thickness'] = int(thickness * 1000)
    # show_section_shape(point_list, hull=hull)
  arm_length, leg_length = get_arm_leg_length(point_set)
  measure_dir['arm_length'] = int(arm_length * 1000)
  measure_dir['leg_length'] = int(leg_length * 1000)
  measure_dir['shoulder_width'] = int(_getShoulderWidth(point_set) * 1000)
  height, bottom_val = _getHeight(point_set)
  measure_dir['height'] = int(height * 1000)

  temp = model_file.split('\\')
  measure_dir['filename'] = temp[len(temp) - 1].replace('.obj', '').replace('SPRING', '')

  return measure_dir

if __name__=='__main__':
  # save_part_index2txt()
  pprint.pprint(get_model_all_measure_value('model_example/SPRING0013.obj'))
  